﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NSRE.Math;

namespace Mosos
{
    class Cell: Cell_interface
    {
        private Vector2 position;
        private Vector2 acceleration;
        private Vector2 velocity;
        private float energy;
        private float radius;
        private bool alive = true;
        private MGraphics.World world;
        private float delta_energy = 0;


        const float MAX_SPEED = 1000.0f;
        const float TIME_WEIGHT = 1000.0f;
        const float MIN_ENERGY = 50.0f;
        const float EATING_RATE = 30.0f;
        const float FORCE_MASS_RATIO = 100.1f;
        const float MASS_LOSE_RATE = 2.0f;
        const float ELAST_WALL = 0.7f;
        const float ELAST_CELL_EQUAL = 0.8f;
        const float ELAST_CELL_DIFF = 0.0017f;
        const float FRICTION = 0.0002f;
        const float ENERGY_FORCE_RATIO = 0.2f;
        const float FORCE_FACTOR = 300f;

        private List<string> messages;
        const int MAX_MSG = 100;
        const int MAX_LEN_MSG = 2000;

        private string name;
        AI_interface ai_inter;

        // CE QUE VOUS ALLEZ UTILISER //
        public float getRadius()
        {
            return radius;
        }

        public void SendMessage (string msg)
        {
            if (msg.Length < MAX_MSG && msg.Length < 2000)
                messages.Add(msg);
        }

        public List<string> GetMessages()
        {
            return messages;
        }

        public void ClearMessages()
        {
            messages.Clear();
        }
        public Vector2 getPosition()
        {
            return position;
        }

        public Vector2 getVelocity()
        {
            return velocity;
        }

        public Vector2 getAcceleration()
        {
            return acceleration;
        }

        public float getEnergy()
        {
            return energy;
        }
        public void push(Vector2 force)
        {
            acceleration = force * FORCE_FACTOR / energy;
            energy -= force.Magnitude() * ENERGY_FORCE_RATIO;
            update_radius();
        }

        // C'est tout ! //

        public Cell(Vector2 pos, float rad, AI_interface ai, string cell_name, MGraphics.World w)
        {
            ai_inter = ai;
            position = pos;
            radius = rad;
            name = cell_name;
            messages = new List<string>();
            acceleration = new Vector2(0, 0);
            velocity = new Vector2(0, 0);
            energy = rad * rad * (float)Math.PI;
            rad *= 2;
            world = w;
            world.Add_Cell(name);
            world.Set_Cell_Position(name, pos.x, pos.y);
            world.Set_Cell_Size(name, rad);
            if (ai.get_path_image() != null)
                world.Set_Cell_Texture(name, ai.get_path_image());
        }

      

        public void update(float elapsed_time)
        {
            elapsed_time /= TIME_WEIGHT;
            if (velocity.Magnitude() < MAX_SPEED)
                velocity += acceleration * elapsed_time;

            velocity -= velocity * radius * FRICTION;

            float delta = elapsed_time * MASS_LOSE_RATE;
            delta_energy += delta;
            energy -= delta;

            if (delta_energy > 1)
            {
                update_radius();
                delta_energy = 0;
            }

            position += velocity * elapsed_time;
            acceleration = new Vector2(0, 0);
            world.Set_Cell_Position(name, position.x, position.y);
        }

        public void call_AI(Cell[] cells)
        {
            ai_inter.take_decision(cells, this);
        }

       

        public void push_from(Vector2 pos)
        {
            Vector2 diff = position - pos;
            diff.Normalize();
            acceleration = diff * 100;
            energy -= diff.Magnitude() * FORCE_MASS_RATIO;
            update_radius();
        }

        public void collision_wall(float world_width, float world_height)
        {
            float w_2 = world_width / 2;
            Vector2 diff = new Vector2(w_2 - radius, w_2 - radius) - position;
            float diff_mag = diff.Magnitude();
            float penetration = diff_mag + radius - w_2;
            if (penetration > 0)
            {
                diff = diff / diff_mag;
                Vector2 proj_dv = Vector2.Dot(velocity, diff) * (-diff);
                velocity += 2 * ELAST_WALL * proj_dv;
                position += penetration * diff;
            }
        }

        private void update_radius()
        {

            float new_rad = 0;
            if (energy > MIN_ENERGY)
                new_rad = (float)Math.Sqrt((double)energy / Math.PI);
            else
            {
                energy = 0;
                world.Remove_Cell(name);
                alive = false;
            }
            float diff_rad = new_rad - radius;
            radius = Convert.ToInt32(Math.Round(new_rad));
          
            world.Set_Cell_Size(name, new_rad * 2);

            //position.x -= diff_rad;
            //position.y -= diff_rad;

        }

        public bool is_alive()
        {
            return alive;
        }


        private void eat_from(Cell cell2, float quantity)
        {
            quantity *= EATING_RATE;
            if (cell2.energy > 0)
            {
                energy += quantity;
                update_radius();
                cell2.energy -= quantity;
                cell2.update_radius();
            }
        }

        public void collision_cell(Cell cell2)
        {
            Vector2 delta_pos = new Vector2(radius, radius);
            delta_pos -= new Vector2(cell2.radius, cell2.radius);
       

            Vector2 diff = position - cell2.position + delta_pos;
            float diff_length = diff.Magnitude();

            float sum_rad = radius + cell2.radius;
            float penetration = sum_rad - diff_length;
            if (diff_length > 0 &&  penetration > 0)
            {
                
                if (radius > cell2.radius)
                    eat_from(cell2, penetration);
                else if (radius < cell2.radius)
                    cell2.eat_from(this, penetration);

                diff.Normalize();
                Vector2 delta_velocity_1 = velocity - cell2.velocity;
                Vector2 delta_velocity_2 = cell2.velocity - velocity;
                Vector2 pen_vec = (sum_rad - diff_length + 1) * diff;

               
                if (radius == cell2.radius)
                {
                    position += pen_vec;
                    Vector2 proj_dv = Vector2.Dot(-diff, delta_velocity_2) * (-diff) * cell2.energy / energy;
                    velocity += proj_dv * ELAST_CELL_EQUAL;

                    proj_dv = Vector2.Dot(diff, delta_velocity_1) * (diff) * energy / cell2.energy;
                    cell2.velocity += proj_dv * ELAST_CELL_EQUAL;
                }
                else
                {
                    Vector2 proj_dv = Vector2.Dot(-diff, delta_velocity_2) * (-diff) * cell2.energy / energy;
                    velocity += proj_dv * ELAST_CELL_DIFF;

                    proj_dv = Vector2.Dot(diff, delta_velocity_1) * (diff) * energy / cell2.energy;
                    cell2.velocity += proj_dv * ELAST_CELL_DIFF;
                }
            }
              
        
          
        }
     
    }

}
